package io.xxx.xbutler.core.callback;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.hy.corecode.idgen.WFGIdGenerator;
import io.xxx.xbutler.constant.Formats;
import io.xxx.xbutler.constant.Topics;
import io.xxx.xbutler.data.CallbackMapper;
import io.xxx.xbutler.domain.Callback;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.pulsar.core.PulsarTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

@Slf4j
@RestController
@RequestMapping("/callback")
public class CallbackReceiver {

    private static final NumberFormat numberFormat = new DecimalFormat("00000000");

    private final Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);

    @Resource
    private WFGIdGenerator idGenerator;

    @Resource
    private CallbackMapper callbackMapper;

    @Resource
    private PulsarTemplate<Long> pulsarTemplate;

    @Value("${callback.data.directory:cdata}")
    private String cdataDirectory;

    @RequestMapping
    public void receive(@RequestParam("strSign") String sign,
                        @RequestParam("nType") Integer type,
                        @RequestParam("strContext") String context) {
        log.debug("收到回调[sign:{}, type:{}, context:{}]", sign, type, context);
        JSONObject json = JSON.parseObject(context);
        if (type == null) {
            type = json.getInteger("nType");
        }
        long id = idGenerator.next();
        String serialNo = json.getString("vcSerialNo");
        Callback callback = new Callback()
                .setId(id)
                .setMerchantNo(json.getString("vcMerchantNo"))
                .setType(type)
                .setSerialNo(serialNo)
                .setRobotId(json.getString("vcRobotWxId"))
                .setRobotSerialNo(json.getString("vcRobotSerialNo"))
                .setCode(json.getInteger("nResult"))
                .setMessage(json.getString("vcResult"))
                .setContext(JSON.parseObject(context))
                .setData(json.get("Data"))
                .setSign(sign)
                .setCreatedTime(LocalDateTime.now());
        Instant timestamp = json.getInstant("nTimeStamp");
        if (timestamp != null) {
            callback.setTimestamp(LocalDateTime.ofInstant(timestamp, ZoneOffset.systemDefault()));
        }

        callbackMapper.insert(callback);
        pulsarTemplate.send(Topics.CALLBACK, id);

        // 保存文件，方便回放
        executor.execute(() -> {
            try {
                LocalDate now = LocalDate.now();
                String filename = now.format(Formats.COMPACT_DATE_FORMATTER);
                Path dir = Paths.get(cdataDirectory);
                Path file = Paths.get(cdataDirectory, filename);
                if (!Files.exists(dir)) {
                    Files.createDirectory(dir);
                }
                if (!Files.exists(file)) {
                    Files.createFile(file);
                }

                try (BufferedWriter writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8, StandardOpenOption.APPEND)) {
                    writer.write(id + sign + numberFormat.format(context.length()) + context);
                    writer.newLine();
                }
            } catch (IOException e) {
                log.error("保存文件失败[id:{}, serialNo:{}]", id, serialNo, e);
                throw new RuntimeException(e);
            }
        });
    }
}
